/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.debugger.jpda;
import java.beans.PropertyEditor;
import java.text.MessageFormat;
import java.util.ResourceBundle;
import java.util.List;
import java.util.LinkedList;
import javax.swing.SwingUtilities;
import javax.swing.JComponent;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Location;
import com.sun.jdi.Value;
import com.sun.jdi.Field;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.ClassNotPreparedException;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.InvalidLineNumberException;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.request.BreakpointRequest;
import com.sun.jdi.request.EventRequestManager;
import com.sun.jdi.request.ClassPrepareRequest;
import com.sun.jdi.request.AccessWatchpointRequest;
import com.sun.jdi.request.ModificationWatchpointRequest;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.event.BreakpointEvent;
import com.sun.jdi.event.ClassPrepareEvent;
import com.sun.jdi.event.WatchpointEvent;
import com.sun.jdi.event.AccessWatchpointEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.EventSet;
import com.sun.jdi.event.EventIterator;
import com.sun.jdi.event.StepEvent;
import org.openide.TopManager;
import org.openide.filesystems.FileObject;
import org.openide.explorer.propertysheet.editors.ChoicePropertyEditor;
import org.openide.text.Line;
import org.openide.nodes.Node;
import org.openide.nodes.PropertySupport;
import org.openide.util.NbBundle;
import org.openide.util.actions.SystemAction;
import org.netbeans.modules.debugger.support.ClassBreakpointEvent;
import org.netbeans.modules.debugger.support.StopEvent;
import org.netbeans.modules.debugger.support.StopAction;
import org.netbeans.modules.debugger.support.PrintAction;
import org.netbeans.modules.debugger.support.CoreBreakpoint;
import org.netbeans.modules.debugger.support.AbstractThread;
import org.netbeans.modules.debugger.support.AbstractVariable;
import org.netbeans.modules.debugger.support.CallStackFrame;
import org.netbeans.modules.debugger.support.actions.AddBreakpointAction;
import org.netbeans.modules.debugger.jpda.util.*;
/**
* Implementation of breakpoint on variable accecc / modification.
*
* @author Jan Jancura
*/
public class VariableBreakpoint extends ClassBreakpointEvent implements Executor, StopEvent {
// static ....................................................................................
/** Property name constant. */
public static final String PROP_FIELD_NAME = "fieldName"; // NOI18N
/** Property name constant. */
public static final String PROP_FIELD = "field"; // NOI18N
/** Property name constant. */
public static final String PROP_TYPE = "type"; // NOI18N
/** Property type value constant. */
public static final int ACCESS_TYPE = 0;
/** Property type value constant. */
public static final int MODIFICATION_TYPE = 1;
// variables ....................................................................................
/** Thread which stops on this breakpoint. */
private transient ThreadReference thread;
/** Stores all EventRequests produced by this Event. */
private Requestor requestor;
private transient Field field;
private String fieldName = ((AddBreakpointAction)
SystemAction.get (AddBreakpointAction.class)).getCurrentFieldName ();
private int type = MODIFICATION_TYPE;
private transient AbstractVariable value;
private transient String action;
// Event impl ......................................................................................
/**
* Returns the new instance of Breakpoint.Event.
*/
public CoreBreakpoint.Event getNewInstance () {
return new VariableBreakpoint ();
}
/**
* Sets breakpoint with specified properties.
*/
public boolean set () { //S ystem.out.println ("VariableBreakpoint.set TRY " + this + " in " + getDebugger () ); // NOI18N
JPDADebugger debugger = (JPDADebugger) getDebugger ();
if (debugger.virtualMachine == null) return false;
if ((getClassName () == null) || (getClassName ().trim ().length () < 1)) return false;
if (requestor == null) requestor = new Requestor (debugger.requestManager);
try {
requestor.removeRequests ();
// For unoaded class
ClassPrepareRequest cpr = debugger.requestManager.createClassPrepareRequest (); //S ystem.out.println ("VariableBreakpoint.set CLASSES " + getClassName ()); // NOI18N
cpr.addClassFilter (getClassName ());
cpr.setSuspendPolicy (ClassPrepareRequest.SUSPEND_ALL);
debugger.operator.register (cpr, this);
requestor.add (cpr);
cpr.enable ();
List l = debugger.virtualMachine.classesByName (getClassName ()); //S ystem.out.println ("VariableBreakpoint.set CLASSES " + l.size ()); // NOI18N
if (l.size () == 0) return false;
// Known classes
int i, k = l.size ();
boolean set = false;
for (i = 0; i < k; i++)
if (set ((ReferenceType) l.get (i)))
set = true;
return set;
} catch (VMDisconnectedException e) {
}
return false;
}
/**
* Removes breakpoint.
*/
public void remove () { //S ystem.out.println ("VariableBreakpoint.remove " + this); // NOI18N
if (requestor != null)
requestor.removeRequests ();
}
/**
* Returns specific properties of this event.
*/
public Node.Property[] getProperties () {
final ResourceBundle bundle = NbBundle.getBundle (VariableBreakpoint.class);
return new Node.Property[] {
new PropertySupport.ReadWrite (
CoreBreakpoint.PROP_CLASS_NAME,
String.class,
bundle.getString ("PROP_breakpoint_class_name"),
bundle.getString ("HINT_breakpoint_class_name")
) {
public Object getValue () {
return getClassName ();
}
public void setValue (Object val) throws IllegalArgumentException {
try {
setClassName (((String)val).trim ());
} catch (ClassCastException e) {
throw new IllegalArgumentException ();
}
}
},
new PropertySupport.ReadWrite (
PROP_FIELD_NAME,
String.class,
bundle.getString ("PROP_breakpoint_field_name"),
bundle.getString ("HINT_breakpoint_field_name")
) {
public Object getValue () throws IllegalArgumentException {
return getFieldName ();
}
public void setValue (Object val) throws IllegalArgumentException {
try {
setFieldName (((String) val).trim ());
} catch (ClassCastException e) {
throw new IllegalArgumentException ();
}
}
},
new PropertySupport.ReadWrite (
PROP_TYPE,
Integer.TYPE,
bundle.getString ("PROP_breakpoint_type_name"),
bundle.getString ("HINT_breakpoint_type_name")
) {
public Object getValue () throws IllegalArgumentException {
return new Integer (getType ());
}
public void setValue (Object val) throws IllegalArgumentException {
try {
setType (((Integer) val).intValue ());
} catch (ClassCastException e) {
throw new IllegalArgumentException ();
}
}
public PropertyEditor getPropertyEditor () {
return new ChoicePropertyEditor (
new int[] {
ACCESS_TYPE,
MODIFICATION_TYPE
},
new String[] {
bundle.getString ("CTL_Breakpoint_type_access_value"),
bundle.getString ("CTL_Breakpoint_type_modification_value")
}
);
}
}
};
}
/**
* Returns actions available specially for this version of event.
*/
public CoreBreakpoint.Action[] getBreakpointActions () {
CoreBreakpoint.Action[] myActions = new CoreBreakpoint.Action[] {
new StopAction (),
new VariablePrintAction (),
};
CoreBreakpoint.Action[] actions = new CoreBreakpoint.Action [super.getBreakpointActions ().length + myActions.length];
System.arraycopy (super.getBreakpointActions (), 0, actions, 0, super.getBreakpointActions ().length);
System.arraycopy (myActions, 0, actions, super.getBreakpointActions ().length, myActions.length);
return actions;
}
/**
* Returns lines to highlite in the editor.
*/
public Line[] getLines () {
return null;
}
/**
* Returns name of type of this event.
*/
public String getTypeName () {
return "Variable"; // NOI18N
}
/**
* Returns display name of this event.
*/
public String getTypeDisplayName () {
return NbBundle.getBundle (VariableBreakpoint.class).getString ("CTL_Variable_event_type_name");
}
/**
* Returns display name of this instance of event. It will be used
* as the name of the breakpoint.
*/
public String getDisplayName () {
if (type == MODIFICATION_TYPE)
return new MessageFormat (
NbBundle.getBundle (VariableBreakpoint.class).getString ("CTL_Variable_modification_event_name")
).format (new Object[] {getClassName (), getFieldName ()});
else
return new MessageFormat (
NbBundle.getBundle (VariableBreakpoint.class).getString ("CTL_Variable_access_event_name")
).format (new Object[] {getClassName (), getFieldName ()});
}
/**
* Returns name of icon.
*/
public String getIconBase () {
return "/org/netbeans/modules/debugger/resources/breakpointOnVariable"; // NOI18N
}
/**
* Returns customizer visual component.
*/
public JComponent getCustomizer () {
return new VariableBreakpointPanel (this);
}
/**
* Aditional ifno about debugger state when this event occures.
* If event do not produce this type of info, null is returned.
*/
public AbstractThread getThread () {
JPDADebugger debugger = (JPDADebugger) getDebugger ();
return debugger.threadManager.getThread (thread);
}
/**
* Aditional ifno about debugger state when this event occures.
* If event do not produce this type of info, null is returned.
*/
public CallStackFrame[] getCallStack () {
return getThread ().getCallStack ();
}
/**
* Aditional ifno about debugger state when this event occures.
* If event do not produce this type of info, null is returned.
*/
public AbstractVariable getVariable () {
return value;
}
// interface Executor .....................................................................
/**
* Executes breakpoint hit event.
*/
public void exec (com.sun.jdi.event.Event event) { //S ystem.out.println ("VariableBreakpoint.exec! " + this + " : " + event); // NOI18N
if (event instanceof ClassPrepareEvent) {
ReferenceType tryClass = ((ClassPrepareEvent) event).referenceType ();
boolean v = set (tryClass);
if (v && !getBreakpoint ().isValid ()) setValid (true);
((JPDADebugger) getDebugger ()).operator.resume ();
return;
}
thread = ((WatchpointEvent) event).thread ();
value = new JPDAVariable (
(JPDADebugger) getDebugger (),
"Current value", // NOI18N
((WatchpointEvent) event).valueCurrent (),
"" // NOI18N
);
action = (event instanceof AccessWatchpointEvent) ?
NbBundle.getBundle (VariableBreakpoint.class).getString ("CTL_Access") :
NbBundle.getBundle (VariableBreakpoint.class).getString ("CTL_Modification");
perform ();
}
// StopEvent impl ......................................................................................
/**
* Performs stop action.
*/
public void stop (boolean stop) {
((JPDADebugger) getDebugger ()).stop (stop, getThread ());
}
// other methods .........................................................................
/**
* Set name of class the field is in.
*/
public void setClassName (String cn) {
if (className == null) {
if (cn == null) return;
} else
if ((cn != null) && cn.equals (className)) return;
Object old = className;
className = cn;
field = null;
firePropertyChange (CoreBreakpoint.PROP_CLASS_NAME, old, className);
}
/**
* Returns name of field in the current class.
*/
public String getFieldName () {
return fieldName;
}
/**
* Sets name of field in the current class.
*/
public void setFieldName (String name) {
if (fieldName == null) {
if (name == null) return;
} else
if ((name != null) && name.equals (fieldName)) return;
String old = fieldName;
fieldName = name;
field = null;
firePropertyChange (PROP_FIELD_NAME, old, fieldName);
}
/**
* Returns current field.
*/
public Field getField () {
return field;
}
/**
* Sets field and className and fieldName.
*/
public void setField (Field field) {
if (this.field == field) return;
Field old = field;
this.field = field;
firePropertyChange (PROP_FIELD, old, field);
if (field == null) return;
try {
setClassName (field.declaringType ().name ());
setFieldName (field.type ().name ());
} catch (ObjectCollectedException e) {
} catch (ClassNotLoadedException e) {
} catch (VMDisconnectedException e) {
}
}
/**
* Returns current field.
*/
public int getType () {
return type;
}
/**
* Sets field and className and fieldName.
*/
public void setType (int type) {
if (this.type == type) return;
if ( (type != MODIFICATION_TYPE) &&
(type != ACCESS_TYPE)
) throw new IllegalArgumentException ();
int old = type;
this.type = type;
firePropertyChange (PROP_TYPE, new Integer (old), new Integer (type));
}
/**
* Sets breakpoint for given class.
*/
public boolean set (ReferenceType clazz) {
JPDADebugger debugger = (JPDADebugger) getDebugger ();
if (debugger.virtualMachine == null) return false;
try {
Field f = null;
boolean ok = false;
try {
f = clazz.fieldByName (getFieldName ());
} catch (ClassNotPreparedException e) { //S ystem.out.println ("VariableBreakpoint " + e); // NOI18N
} catch (ObjectCollectedException e) { //S ystem.out.println ("VariableBreakpoint " + e); // NOI18N
} //S ystem.out.println ("VariableBreakpoint.set classinstance: " + rt + " field: " + getFieldName () + " field: " + f); // NOI18N
if (f == null) return false;
if (type == ACCESS_TYPE) {
AccessWatchpointRequest awr = debugger.requestManager.createAccessWatchpointRequest (f);
awr.setSuspendPolicy (AccessWatchpointRequest.SUSPEND_ALL);
awr.addClassFilter ("*"); // NOI18N
debugger.operator.register (awr, this);
requestor.add (awr);
awr.enable (); //S ystem.out.println ("VariableBreakpoint.OK! a"); // NOI18N
ok = true;
} else {
ModificationWatchpointRequest mwr = debugger.requestManager.createModificationWatchpointRequest (f);
mwr.setSuspendPolicy (ModificationWatchpointRequest.SUSPEND_ALL);
mwr.addClassFilter ("*"); // NOI18N
debugger.operator.register (mwr, this);
requestor.add (mwr);
mwr.enable (); //S ystem.out.println ("VariableBreakpoint.OK! m"); // NOI18N
ok = true;
}
return ok;
} catch (VMDisconnectedException e) {
}
return false;
}
public String toString () {
return "JPDAVariableBreakpoint " + getClassName () + "." + getFieldName () + "." + getType (); // NOI18N
}
// innerclasses ......................................................................................
class VariablePrintAction extends PrintAction {
/**
* Creates the new Thrad Print action with default text.
*/
VariablePrintAction () {
super (
NbBundle.getBundle (VariableBreakpoint.class).getString ("CTL_Variable_print_name")
);
}
/**
* Returns new initialized instance of Thrad Print action.
*/
protected CoreBreakpoint.Action getNewInstance () {
return new VariablePrintAction ();
}
/**
* Resolving special tags:
* variableName name variable
* action name of action
*/
protected void resolveTag (String tag, CoreBreakpoint.Event event, StringBuffer sb) {
if (tag.equals ("variableName")) // NOI18N
sb.append (((VariableBreakpoint) event).getFieldName ());
else
if (tag.equals ("action")) // NOI18N
sb.append (((VariableBreakpoint) event).action);
else
super.resolveTag (tag, event, sb);
}
}
}
/*
* Log
* 10 Gandalf-post-FCS1.8.4.0 3/28/00 Daniel Prusa
* 9 Gandalf 1.8 1/14/00 Daniel Prusa NOI18N
* 8 Gandalf 1.7 1/13/00 Daniel Prusa NOI18N
* 7 Gandalf 1.6 1/4/00 Jan Jancura Use trim () on user
* input.
* 6 Gandalf 1.5 12/10/99 Jan Jancura Breakpoint on variable
* still do not work - bug in JPDA implementation.
* 5 Gandalf 1.4 11/8/99 Jan Jancura Somma classes renamed
* 4 Gandalf 1.3 11/5/99 Jan Jancura Empty values support
* 3 Gandalf 1.2 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 2 Gandalf 1.1 9/28/99 Jan Jancura
* 1 Gandalf 1.0 9/28/99 Jan Jancura
* $
*/